package com.hy.dp.stock;

public class BuySaleStock02 {


    /**
     * 122.买卖股票的最佳时机II
     * 力扣题目链接
     *
     * 给定一个数组，它的第 i 个元素是一支给定股票第 i 天的价格。
     *
     * 设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易（多次买卖一支股票）。
     *
     * 注意：你不能同时参与多笔交易（你必须在再次购买前出售掉之前的股票）。
     *
     * 示例 1:
     * 输入: [7,1,5,3,6,4]
     * 输出: 7
     * 解释: 在第 2 天（股票价格 = 1）的时候买入，在第 3 天（股票价格 = 5）的时候卖出, 这笔交易所能获得利润 = 5-1 = 4。随后，在第 4 天（股票价格 = 3）的时候买入，在第 5 天（股票价格 = 6）的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
     *
     *
     * 示例 3:
     * 输入: [7,6,4,3,1]
     * 输出: 0
     * 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
     * 思路
     * 本题我们在讲解贪心专题的时候就已经讲解过了贪心算法：买卖股票的最佳时机II，只不过没有深入讲解动态规划的解法，那么这次我们再好好分析一下动规的解法。
     *
     * 在动规五部曲中，这个区别主要是体现在递推公式上，其他都和121. 买卖股票的最佳时机一样一样的。
     *
     * 所以我们重点讲一讲递推公式。
     *
     * 1.这里重申一下dp数组的含义：
     *
     * dp[i][0] 表示第i天持有股票所得现金。
     * dp[i][1] 表示第i天不持有股票所得最多现金
     * 如果第i天持有股票即dp[i][0]， 那么可以由两个状态推出来
     *
     * 第i-1天就持有股票，那么就保持现状，所得现金就是昨天持有股票的所得现金 即：dp[i - 1][0]
     * 第i天买入股票，所得现金就是昨天不持有股票的所得现金减去 今天的股票价格 即：dp[i - 1][1] - prices[i]
     *
     * 在121. 买卖股票的最佳时机中，因为股票全程只能买卖一次，所以如果买入股票，那么第i天持有股票即dp[i][0]一定就是 -prices[i]。
     *
     * 而本题，因为一只股票可以买卖多次，所以当第i天买入股票的时候，所持有的现金可能有之前买卖过的利润。
     *
     * 那么第i天持有股票即dp[i][0]，如果是第i天买入股票，所得现金就是昨天不持有股票的所得现金 减去 今天的股票价格 即：dp[i - 1][1] - prices[i]。
     *
     * 在来看看如果第i天不持有股票即dp[i][1]的情况， 依然可以由两个状态推出来
     *
     * 第i-1天就不持有股票，那么就保持现状，所得现金就是昨天不持有股票的所得现金 即：dp[i - 1][1]
     * 第i天卖出股票，所得现金就是按照今天股票佳价格卖出后所得现金即：prices[i] + dp[i - 1][0]
     * @param prices
     * @return
     */
    // 实现1：二维数组存储
    // 可以将每天持有与否的情况分别用 dp[i][0] 和 dp[i][1] 来进行存储
    // 时间复杂度：O(n)，空间复杂度：O(n)
    public static int maxProfit(int [] prices){
        int len = prices.length;
        // 创建二维数组存储状态
        int [][] dp = new int[len][2];
        // 初始化
        dp[0][0] = 0;
        // 持有股票
        dp[0][1] = -prices[0];

        for (int i = 1; i < len; i++) {
            // 第 i 天，没有股票
            dp[i][0] = Math.max(dp[i - 1][0],dp[i-1][1] + prices[i]);
            // 第 i 天，持有股票
            dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0] - prices[i]);
        }
        // 卖出股票收益高于持有股票收益，因此取[0]
        return dp[len - 1][0];
    }

    // 优化空间
    public static int maxProfit02(int [] prices){
        int [] dp = new int[2];
        // 0表示持有，1表示卖出
        dp[0] = -prices[0];
        dp[1] = 0;
        for (int i = 1; i <= prices.length; i++) {
            // 前一天持有; 既然不限制交易次数，那么再次买股票时，要加上之前的收益
            dp[0] = Math.max(dp[0],dp[1] - prices[i-1]);
            // 前一天卖出; 或当天卖出，当天卖出，得先持有
            dp[1] = Math.max(dp[1],dp[0] + prices[i-1]);
        }
        return dp[1];
    }

    public static void main(String[] args) {
        int [] prices = {7,1,5,3,6,4};
        System.out.println("res: "+maxProfit(prices));
        System.out.println("res: "+maxProfit02(prices));
    }
}
